/*
 * Packet.cpp
 *
 *  Created on: 2.4.2011
 *      Author: Tomas Srna
 */

#include "Packet.h"

Packet::Packet() {}

Packet::Packet(const char * buf, int len)
{
	if(len < 2 ) return ;

	int i = 0;

	opcode = (type)0;

	opcode = (type)(opcode|buf[i++]);
	opcode = (type)(opcode<<8);
	opcode = (type)(opcode|buf[i++]);

	switch(opcode) {
	case RRQ:
	case WRQ:
	{
		filename.clear();
		while(i < len && buf[i]) {
			filename.push_back(buf[i++]);
		}
		i++;
		mode.clear();
		while(i < len && buf[i]) {
			mode.push_back(buf[i++]);
		}
		i++;
		break;
	}
	case DATA:
	{
		// Block #
		blocknum = 0;
		blocknum = (blocknum|buf[i++]);
		blocknum = (blocknum<<8);
		blocknum = (blocknum|buf[i++]);

		// Data
		data.clear();
		while(i<len)
			data.push_back(buf[i++]);
		break;
	}
	case ACK:
	{
		// Block #
		blocknum = 0;
		blocknum = (blocknum|buf[i++]);
		blocknum = (blocknum<<8);
		blocknum = (blocknum|buf[i++]);
		break;
	}
	case ERROR:
	{
		// ErrorCode
		errorcode = 0;
		errorcode = (errorcode|buf[i++]);
		errorcode = (errorcode<<8);
		errorcode = (errorcode|buf[i++]);

		// ErrMsg
		errmsg.clear();
		while(i<len)
			errmsg.push_back(buf[i++]);
		break;
	}
	default:
		break;
	}
}

void Packet::store(std::vector<char>& buf) const {
	buf.clear();

	// Opcode
	buf.push_back((char)((int)(opcode>>8)&0xF));
	buf.push_back((char)((int)opcode&0xF));

	switch(opcode) {
	case RRQ:
	case WRQ:
	{
		unsigned int i;
		const char * c_filename = filename.c_str();
		for(i = 0; i < filename.size(); ++i) {
			buf.push_back(c_filename[i]);
		}
		buf.push_back('\0');
		const char * c_mode = mode.c_str();
		for(i = 0; i < mode.size(); ++i) {
			buf.push_back(c_mode[i]);
		}
		buf.push_back('\0');
		break;
	}
	case DATA:
	{
		// Block #
		buf.push_back((char)((int)(blocknum>>8)&0xF));
		buf.push_back((char)((int)blocknum&0xF));

		// Data
		unsigned int i;
		const char * c_data = &data[0];
		for(i = 0; i < data.size(); ++i) {
			buf.push_back(c_data[i]);
		}
		break;
	}
	case ACK:
	{
		// Block #
		buf.push_back((char)((int)(blocknum>>8)&0xF));
		buf.push_back((char)((int)blocknum&0xF));
		break;
	}
	case ERROR:
	{
		// ErrorCode
		buf.push_back((char)((int)(errorcode>>8)&0xF));
		buf.push_back((char)((int)errorcode&0xF));

		// ErrorMessage
		unsigned int i;
		const char * c_errmsg = errmsg.c_str();
		for(i = 0; i < errmsg.size(); ++i) {
			buf.push_back(c_errmsg[i]);
		}
		break;
	}
	default:
		break;
	}
}

void Packet::print(std::ostream& logger) {
	std::vector<char> v;
	store(v);
	std::vector<char>::iterator i;
	for(i = v.begin(); i != v.end(); ++i) {
		logger << "0x" << std::hex
				<< std::setw(2) << std::setfill('0')
				<< (int)*i << " ";
	}
	//logger << std::resetiosflags;
}

std::string Packet::t(type opcode)
{
	switch(opcode)
	{
	case RRQ:
		return "RRQ";
	case WRQ:
		return "WRQ";
	case DATA:
		return "DATA";
	case ACK:
		return "ACK";
	case ERROR:
		return "ERROR";
	default:
		return "";
	}
}

std::ostream& operator << (std::ostream& os, const Packet& rhs)
{
	os << "[Packet Type: " << Packet::t(rhs.opcode);
	if(rhs.opcode == Packet::RRQ || rhs.opcode == Packet::WRQ)
	{
		os << "; Filename: " << rhs.filename;
		os << "; Mode: " << rhs.mode;
	}
	if(rhs.opcode == Packet::DATA || rhs.opcode == Packet::ACK)
	{
		os << "; Block #: " << rhs.blocknum;
	}
	if(rhs.opcode == Packet::DATA)
	{
		os << "; Data: " << rhs.data.size() << "bytes ";
	}
	if(rhs.opcode == Packet::ERROR)
	{
		os << "; Error Code: " << rhs.errorcode;
		os << "; Error Msg: " << rhs.errmsg;
	}
	os << "]";
	return os;
}
